package com.demo.webapp.ui.menu.displayer;

import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

import org.apache.commons.lang.StringUtils;
import org.apache.struts2.views.freemarker.ScopesHashModel;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import com.demo.common.RegexUtils;
import com.demo.webapp.ui.ComponentBase;
import com.demo.webapp.ui.access.DisplayerAuthority;
import com.demo.webapp.ui.access.PermissionsAdapter;
import com.demo.webapp.ui.access.impl.RolesPermissionsAdapter;
import com.demo.webapp.ui.displayer.FreemarkerDisplayerSupport;
import com.demo.webapp.ui.menu.MenuComponent;
import com.demo.webapp.ui.menu.MenuComponent.Type;

import freemarker.template.Template;
import freemarker.template.TemplateModel;

/**
 * FreemarkerMenuDisplayer
 * 使用freemarker作为Menu的渲染对象;<br>
 * 该对象依赖于Struts2。使用FreemarkerManager对象进行管理<br>
 * 请参照Struts2的Freemarker配置说明<br>
 * FreemarkerMenuDisplayer提供的参数：
 * <ol>
 *	<li>ognl : {@link org.apache.struts2.views.jsp.ui.OgnlTool}</li>
 *	<li>action</li>
 *	<li>request</li>
 *	<li>session</li>
 *	<li>application</li>
 *	<li>menu</li>
 *	<li>displayer</li>
 *	<li>menuId</li>
 * </ol>
 * <code>MenuDisplayer</code>
 * <p>
 * 例如：
 * </p><pre>   
 *&lt;menu:menuDisplayer name="Freemarker" config="/WEB-INF/menu/cssVerticalMenu.ftl" permissions="rolesAdapter"&gt;
 *	&lt;menu:menu name="AdminMenu"/&gt;
 *&lt;/menu:menuDisplayer&gt;</pre>
 *<p>
 *<ul><li>name<br>
 *	映射为使用menu-config.xml中配置的Displayer名称；该配置freemarker渲染该menu内容</li>
 *	<li>config<br>
 *	使用哪种模板文件渲染Menu『config的基础路径为classpath.』</li>
 *</ul>
 *</p> 
 *      
 * @author ryuu.kk
 * @version 1.0
 */
public class FreemarkerMenuDisplayer extends FreemarkerDisplayerSupport<MenuComponent> implements DisplayerAuthority<Authentication>{

    /**
     * 默认导航分隔符
     */
    protected static String BREAD_CRUMB_DELIMITER = " > ";
    
    protected PermissionsAdapter<Authentication> permissionsAdapter = null;
    
    private Authentication authentication;
	@Override
	public String getType() {
		//return Type.menu.toString();
		return null;
	}

    /**
     * 渲染展示
     * Menu容器对象,该对象是一个链式对象。实现多层树形Menu
     * 
     * @param menu Menu模型
     */
	@Override
	public void display(MenuComponent menu) throws JspException, IOException {
		//Authentication config from spring security
		authentication = SecurityContextHolder.getContext().getAuthentication();
		if (permissionsAdapter == null) {
			// 默认使用基于角色的安全访问适配器
			this.permissionsAdapter = new RolesPermissionsAdapter(getServletRequest());
		}
		//不需要权限认证&用户有访问许可时,可以访问
		boolean permise = getPermissionsAdapter() == null? true : getPermissionsAdapter().hasPermission(menu, authentication);
        if (permise) {
			super.display(menu);
        }
	}
	
	@Override
	public void end(PageContext pageContext) throws JspException {
		//
	}
	
	/**
	 * 模板的前置处理方法
	 * 该方法将Template提供参数
	 * @param pageContext PageContext
	 * @param menu MenuComponent
	 * @param template FreemarkerTemplate
	 * @param templateModel TemplateModel
	 * @return boolean
	 */
	@Override
	protected boolean preTemplateProcess(MenuComponent menu, Template template, TemplateModel templateModel) {
		boolean result = true;

		ScopesHashModel hash = (ScopesHashModel) templateModel;
		//add something.
		hash.put("menu", menu);
        if (getPageContext().getAttribute("menuId") != null) {
        	hash.put("menuId", getPageContext().getAttribute("menuId"));
        }
        hash.put("displayer", this);
        // 当前URL地址
        hash.put("url", getServletRequest().getRequestURI());
        
		return result;
	}
	
	/**
	 * 判断当前请求地址是否被POJO模型所匹配
	 * 默认已通配符形式匹配
	 * @param menu MenuComponent
	 * @return boolean
	 */
	public boolean parser(MenuComponent menu) {
		// 组成原始URL进行判断;URL将不带有项目名
		return parser(menu, getServletRequest().getServletPath() + "?" + getServletRequest().getQueryString(), Type.pattern);
	}
	
	/**
	 * 判断参数url地址是否被当前POJO模型所匹配
	 * 默认已通配符形式匹配
	 * @param menu MenuComponent
	 * @param url url地址
	 * @return boolean
	 */
	public boolean parser(MenuComponent menu, String url) {
		return parser(menu, url, Type.pattern);
	}
	
	/**
	 * 判断参数url地址是否被当前POJO模型所匹配
	 * @param url url地址
	 * @param type 匹配类型@see com.demo.webapp.ui.menu.Type
	 * @return boolean 
	 */
	public boolean parser(MenuComponent menu, String url, Type type) {
		
		boolean isMatch = false;
		switch(type) {
			case pattern:
				for (String ms : getPatternCache(menu)) {
					isMatch = RegexUtils.wildcardMatch(ms, url);
					if (isMatch) {
						return true;
					}
				}
				break;
			case regular:
				for (String ms : getPatternCache(menu)) {
					isMatch = Pattern.matches(ms, url);
					if (isMatch) {
						return true;
					}
				}
				break;
			default:
			// 找不到?
		}
		return isMatch;
	}
	
	private List<String> getPatternCache(MenuComponent menu) {
		List<String> patternCache = menu.getPatternCache();
		if (patternCache.size() > 0) {
			return patternCache;
		}
		if (StringUtils.isNotBlank(menu.getPattern())) {
			String[] ps = menu.getPattern().split(";");
			for (String s : ps) {
				patternCache.add(s);
			}
		}
		return patternCache;
	}
	
	@Override
	public boolean hasPermission(ComponentBase model) {
		return permissionsAdapter == null ? true : permissionsAdapter.hasPermission(model, authentication);
	}

	@Override
	public PermissionsAdapter<Authentication> getPermissionsAdapter() {
		return this.permissionsAdapter;
	}

	@Override
	public void setPermissionsAdapter(PermissionsAdapter<Authentication> permissionsAdapter) {
		this.permissionsAdapter = permissionsAdapter;
	}

	/**
	 * 返回用户认证
	 * @return 用户认证
	 */
	private Authentication getAuthentication() {
		return SecurityContextHolder.getContext().getAuthentication();
	}
}
